package com.nami.logic.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import com.nami.logic.cache.user.OnrUserCache;
import com.nami.logic.cache.user.UserCache;
import com.nami.logic.service.UserService;
import com.nami.store.api.bo.UserDevice;
import com.nami.store.api.bo.UserDeviceDetailInfo;
import com.nami.store.api.bo.UserWrap;
import com.nami.store.api.request.cache.CacheDelReq;
import com.nami.store.api.service.RemoteL2CacheService;
import com.nami.store.api.service.RemoteUserService;
import com.nami.store.api.response.user.UserDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final RemoteL2CacheService cacheService;

    private final RemoteUserService userService;


    @Override
    public UserWrap getUserById(String appKey, String userId) {
        if (StringUtils.isBlank(appKey) || StringUtils.isBlank(userId)) {
            return null;
        }
        UserWrap user = UserCache.get(userId);
        if (user == null) {
            RLock lock = RedisUtils.getClient().getLock("nami:" + appKey + ":user:lock:" + userId);
            try {
                lock.lock();
                // 只要登录了。就存入了redis
//                UserWrap exist = cacheService.getOnrUser(appKey, userId);
                UserDTO exist = userService.getUserByUserId(appKey, userId);
                if (exist == null) {
                    return null;
                }
                UserWrap userWrap = new UserWrap();
                userWrap.setAppKey(appKey);
                userWrap.setUserId(userId);
                userWrap.setAppKey(exist.getAppKey());
                userWrap.setNick(exist.getNickname());
                userWrap.setAvatar(exist.getAvatarUrl());
                userWrap.setStatus(exist.getStatus());

                UserCache.add(userId, userWrap);
                return userWrap.clone();
            } finally {
                lock.unlock();
            }
        }
        return user;
    }

    @Override
    public Map<String, UserWrap> getUserByIds(String appKey, List<String> userIds) {
        Map<String, UserWrap> map = new HashMap<>();
        if (CollUtil.isEmpty(userIds) || StringUtils.isBlank(appKey)) {
            return map;
        }

        for (String userId : userIds) {
            if (StringUtils.isBlank(userId)) {
                continue;
            }
            UserWrap userFromCache = this.getUserById(appKey, userId);
            if (userFromCache != null) {
                map.put(userId, userFromCache);
            }
        }

        return map;
    }

    @Override
    public UserWrap getOnrUserById(String appKey, String userId) {
        if (StringUtils.isBlank(appKey) || StringUtils.isBlank(userId)) {
            return null;
        }
        UserWrap user = OnrUserCache.get(userId);
        if (user == null) {
            RLock lock = RedisUtils.getClient().getLock("nami:" + appKey + ":user:lock:" + userId);
            try {
                lock.lock();
                // 只要登录了。就存入了redis
//                UserWrap exist = cacheService.getOnrUser(appKey, userId);
                UserWrap exist = cacheService.getOnrUser(appKey, userId);
                if (exist == null) {
                    return null;
                }
                UserWrap userWrap = new UserWrap();
                userWrap.setAppKey(appKey);
                userWrap.setUserId(userId);
                userWrap.setAppKey(exist.getAppKey());
                userWrap.setNick(exist.getNick());
                userWrap.setAvatar(exist.getAvatar());
                userWrap.setStatus(exist.getStatus());
                userWrap.setDeviceInfos(exist.getDeviceInfos());

                OnrUserCache.add(userId, userWrap);
                return userWrap.clone();
            } finally {
                lock.unlock();
            }
        }
        return user;
    }

    @Override
    public Map<String, UserWrap> getOnrUsersByUserIds(String appKey, List<String> userIds) {
        Map<String, UserWrap> map = new HashMap<>();
        if (CollUtil.isEmpty(userIds) || StringUtils.isBlank(appKey)) {
            return map;
        }

        for (String userId : userIds) {
            if (StringUtils.isBlank(userId)) {
                continue;
            }
            UserWrap userFromCache = this.getOnrUserById(appKey, userId);
            if (userFromCache != null) {
                map.put(userId, userFromCache);
            }
        }

        return map;
    }

    public Boolean delUserDeviceFromL1Cache(CacheDelReq request) {
        TimeInterval timer = DateUtil.timer();
        if (StringUtils.isBlank(request.getAppKey()) || StringUtils.isBlank(request.getUserId()) || request.getDeviceType() == null) {
            log.error("[l1-user] 删除一级缓存失败 appKey={}, userId={}", request.getAppKey(), request.getUserId());
            return false;
        }

        // 同登录的key相同
        RLock lock = RedisUtils.getClient().getLock("nami:" + request.getAppKey() + ":user:lock:" + request.getUserId());
        try {
            lock.lock();
            final UserWrap userFromCache = OnrUserCache.get(request.getUserId());
            if (userFromCache == null) {
                return false;
            }

            List<UserDevice> deviceInfos = userFromCache.getDeviceInfos();
            if (CollUtil.isEmpty(deviceInfos)) {
                return false;
            }
            UserDevice userDevice = deviceInfos.stream()
                .filter(item -> request.getDeviceType().equals(item.getDeviceType()))
                .findFirst()
                .orElse(null);
            if (userDevice == null) {
                return false;
            }
            deviceInfos.remove(userDevice);
            UserWrap clone = userFromCache.clone();
            clone.setDeviceInfos(deviceInfos);
            OnrUserCache.add(request.getUserId(), clone);
        } finally {
            lock.unlock();
        }
        // 删除缓存
        cacheService.removeOfflineUser(request);

        if (timer.interval() > 200) {
            log.warn("[l1-user] 删除缓存有问题 userId:{} use={}ms", request.getUserId(), timer.interval());
        }
        return Boolean.TRUE;
    }

    @Override
    public List<UserDevice> getOnlineDevicesByUserId(String appKey, String userId) {
        UserWrap userWrap = OnrUserCache.get(userId);
        if (userWrap == null) {
            // TODO 是否需要锁住
            final UserWrap exist = cacheService.getOnrUser(appKey, userId);
            if (exist == null) {
                return null;
            }
            OnrUserCache.add(userId, exist);
            return exist.getDeviceInfos();
        }
        List<UserDevice> deviceInfos = userWrap.getDeviceInfos();
        if (CollUtil.isEmpty(deviceInfos)) {
            return null;
        }
        return deviceInfos;
    }

    @Override
    public List<UserDevice> getOnlineDevicesExceptThisDevice(String appKey, String userId, Integer deviceType) {
        List<UserDevice> deviceInfos = this.getOnlineDevicesByUserId(appKey, userId);
        if (CollUtil.isEmpty(deviceInfos)) {
            return null;
        }
        if (deviceType == null) {
            return deviceInfos;
        }
        List<UserDevice> result = deviceInfos.stream()
            .filter(item -> !deviceType.equals(item.getDeviceType()))
            .collect(Collectors.toList());
        return result;
    }

    @Override
    public Map<String, List<UserDeviceDetailInfo>> obtainOnlineDevicesByClassifyMqTags(String appKey, List<String> users) {
        TimeInterval timer = DateUtil.timer();
        if (CollUtil.isEmpty(users)) {
            return new HashMap<>();
        }
        List<UserDeviceDetailInfo> list = new ArrayList<>(users.size() * 3);
        UserDeviceDetailInfo item = null;

        List<String> needQuery = new ArrayList<>(users.size());
        for (String userId : users) {
            if (StringUtils.isBlank(userId)) {
                continue;
            }
            UserWrap userWrap = OnrUserCache.get(userId);
            if (userWrap == null) {
                needQuery.add(userId);
                continue;
            }
            List<UserDevice> deviceInfos = userWrap.getDeviceInfos();
            if (CollUtil.isEmpty(deviceInfos)) {
                continue;
            }
            List<UserDevice> deviceInfoList = deviceInfos.stream().distinct().collect(Collectors.toList());
            for (UserDevice deviceInfo : deviceInfoList) {
                item = new UserDeviceDetailInfo();
                item.setUserId(userWrap.getUserId());
                item.setAppKey(userWrap.getAppKey());

                item.setUserIp(deviceInfo.getUserIp());
                item.setDeviceType(deviceInfo.getDeviceType());
                item.setMqTags(deviceInfo.getMqTags());
                list.add(item);
            }

        }

        List<UserWrap> onlineUsers = cacheService.getOnrUsers(appKey, needQuery);
        if (CollUtil.isNotEmpty(onlineUsers)) {
            for (UserWrap onlineUser : onlineUsers) {
                if (onlineUser == null) {
                    continue;
                }
                OnrUserCache.add(onlineUser.getUserId(), onlineUser);
                List<UserDevice> deviceInfos = onlineUser.getDeviceInfos();
                if (CollUtil.isEmpty(deviceInfos)) {
                    continue;
                }
                for (UserDevice deviceInfo : deviceInfos) {
                    if (deviceInfo == null) {
                        continue;
                    }
                    item = new UserDeviceDetailInfo();
                    item.setUserId(onlineUser.getUserId());
                    item.setAppKey(onlineUser.getAppKey());

                    item.setUserIp(deviceInfo.getUserIp());
                    item.setDeviceType(deviceInfo.getDeviceType());
                    item.setMqTags(deviceInfo.getMqTags());
                    list.add(item);
                }
            }
        }

        Map<String, List<UserDeviceDetailInfo>> result = list.stream().collect(Collectors.groupingBy(key -> key.getMqTags()));
        if (timer.interval() > 200) {
            log.warn("[cache] 获取在线用户设备并按长连接分类出现反应慢 use={}ms", timer.interval());
        }
        return result;
    }

    @Override
    public boolean removeOfflineUser(CacheDelReq request) {
        if (request == null) {
            return false;
        }
        if (request.getUserId() == null || StringUtils.isBlank(request.getUserId())
            || StringUtils.isBlank(request.getAppKey())) {
            return false;
        }

        return this.delUserDeviceFromL1Cache(request);
    }


}
